package com.freak.kotlinfitnessserver.factory.notify

import com.freak.kotlinfitnessserver.constant.ByteConstant
import com.freak.kotlinfitnessserver.data.FitnessConfigData
import com.freak.kotlinfitnessserver.factory.INotifyFactory
import com.freak.kotlinfitnessserver.factory.notify.data.BlueRealTimeData
import com.freak.kotlinfitnessserver.factory.notify.data.FlagEntity
import com.freak.kotlinfitnessserver.util.ByteUtil
import com.freak.kotlinfitnessserver.util.LogUtil

class TreadmillNotifyFactory : INotifyFactory {
    companion object {
        const val TAG: String = "TreadmillNotifyFactory"
    }

    private var blueRealTimeData: BlueRealTimeData? = null

    override fun getNotifyByteArray(
        sportStatus: Int,
        fitnessConfigData: FitnessConfigData?
    ): ByteArray {
        return ByteArray(0)
    }

    override fun getNotifyByteArrayList(
        sportStatus: Int,
        fitnessConfigData: FitnessConfigData?
    ): List<ByteArray> {
        //  数组长度会超出20，超出部分需要分包，分包需要对应的flag，需要返回byte数组列表的新式
        // 根据Fitness Machine Feature填充支持的数据
        fitnessConfigData ?: let {
            LogUtil.i(TAG, "fitnessConfigData is null")
            return ArrayList()
        }
        if (blueRealTimeData == null) {
            LogUtil.i(TAG, "init blueRealTimeData")
            blueRealTimeData = BlueRealTimeData()
        }
        configBlueRealTimeDataWithFlag(blueRealTimeData!!, fitnessConfigData)
        val flagEntity = initByteArrayWithBlueRealTimeData(blueRealTimeData!!)
        val list = configByteArrayListWithFlag(
            flagEntity.dataLength,
            flagEntity.flag,
            blueRealTimeData!!
        )
        return list
    }

    private fun configByteArrayListWithFlag(
        dataLength: Int,
        flag: Int,
        blueRealTimeData: BlueRealTimeData
    ): List<ByteArray> {
        LogUtil.i(TAG, "configByteArrayListWithFlag flag = $flag dataLength = $dataLength")
        val resultList = ArrayList<ByteArray>()
        // 需要分包
        if (dataLength > 20) {
            val flagList = getFlagList(ArrayList(), flag)
            for (flagEntity in flagList) {
                val byteArray = configByteArrayWithFlag(
                    flagEntity.dataLength,
                    flagEntity.flag,
                    blueRealTimeData
                )
                resultList.add(byteArray)
            }
            return resultList
        }
        // 不需要分包
        val byteArray = configByteArrayWithFlag(
            dataLength,
            flag,
            blueRealTimeData
        )
        resultList.add(byteArray)
        return resultList
    }

    private fun configByteArrayWithFlag(
        dataLength: Int,
        flag: Int,
        blueRealTimeData: BlueRealTimeData
    ): ByteArray {
        LogUtil.i(TAG, "configByteArrayWithFlag flag = $flag dataLength = $dataLength")
        val byteArray = ByteArray(dataLength)
        var offset = 0
        ByteUtil.intToTwoByteArray(byteArray, flag, offset)
        LogUtil.i(TAG, "flag = $flag")
        offset += 2
        // InstantaneousSpeed
        if (flag and ByteConstant.BYTE_0X01 == 0) {
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mInstantaneousSpeed, offset)
            LogUtil.i(TAG, "instantaneousSpeed = ${blueRealTimeData.mInstantaneousSpeed}")
            offset += 2
        }
        // AverageSpeed
        if (flag and ByteConstant.BYTE_0X02 != 0) {
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mAverageSpeed, offset)
            LogUtil.i(TAG, "averageSpeed = ${blueRealTimeData.mAverageSpeed}")
            offset += 2
        }
        // TotalDistance
        if (flag and ByteConstant.BYTE_0X04 != 0) {
            ByteUtil.intToThreeByteArray(byteArray, blueRealTimeData.mTotalDistance, offset)
            LogUtil.i(TAG, "totalDistance = ${blueRealTimeData.mTotalDistance}")
            offset += 3
        }
        // Inclination and Ramp Angle Setting
        if (flag and ByteConstant.BYTE_0X08 != 0) {
            // Inclination
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mInclination, offset)
            LogUtil.i(TAG, "inclination = ${blueRealTimeData.mInclination}")
            offset += 2
            // RampAngleSetting
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mRampAngleSetting, offset)
            LogUtil.i(TAG, "rampAngleSetting = ${blueRealTimeData.mRampAngleSetting}")
            offset += 2
        }
        // ElevationGain
        if (flag and ByteConstant.BYTE_0X10 != 0) {
            // PositiveElevationGain
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mPositiveElevationGain, offset)
            LogUtil.i(TAG, "positiveElevationGain = ${blueRealTimeData.mPositiveElevationGain}")
            offset += 2
            // NegativeElevationGain
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mNegativeElevationGain, offset)
            LogUtil.i(TAG, "negativeElevationGain = ${blueRealTimeData.mNegativeElevationGain}")
            offset += 2
        }
        // InstantaneousPace
        if (flag and ByteConstant.BYTE_0X20 != 0) {
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mInstantaneousPace, offset)
            LogUtil.i(TAG, "instantaneousPace = ${blueRealTimeData.mInstantaneousPace}")
            offset += 2
        }
        // AveragePace
        if (flag and ByteConstant.BYTE_0X40 != 0) {
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mAveragePace, offset)
            LogUtil.i(TAG, "averagePace = ${blueRealTimeData.mAveragePace}")
            offset += 2
        }
        // Expended Energy
        if (flag and ByteConstant.BYTE_0X80 != 0) {
            // Total Energy
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mTotalEnergy, offset)
            LogUtil.i(TAG, "totalEnergy = ${blueRealTimeData.mTotalEnergy}")
            offset += 2
            // Energy per Hour
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mEnergyPerHour, offset)
            LogUtil.i(TAG, "energyPerHour = ${blueRealTimeData.mEnergyPerHour}")
            offset += 2
            // Energy per Minute
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mEnergyPerMinute, offset)
            LogUtil.i(TAG, "energyPerMinute = ${blueRealTimeData.mEnergyPerMinute}")
            offset += 2
        }
        // HeartRate
        if (flag and ByteConstant.BYTE_0x100 != 0) {
            ByteUtil.intToOneByteArray(byteArray, blueRealTimeData.mHeartRate, offset)
            LogUtil.i(TAG, "heartRate = ${blueRealTimeData.mHeartRate}")
            offset += 1
        }
        // MetabolicEquivalent
        if (flag and ByteConstant.BYTE_0x200 != 0) {
            ByteUtil.intToOneByteArray(byteArray, blueRealTimeData.mMetabolicEquivalent, offset)
            LogUtil.i(TAG, "metabolicEquivalent = ${blueRealTimeData.mMetabolicEquivalent}")
            offset += 1
        }
        // ElapsedTime
        if (flag and ByteConstant.BYTE_0x400 != 0) {
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mElapsedTime, offset)
            LogUtil.i(TAG, "elapsedTime = ${blueRealTimeData.mElapsedTime}")
            offset += 2
        }
        // RemainingTime
        if (flag and ByteConstant.BYTE_0x800 != 0) {
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mRemainingTime, offset)
            LogUtil.i(TAG, "remainingTime = ${blueRealTimeData.mRemainingTime}")
            offset += 2
        }
        // Force on Belt and Power Output
        if (flag and ByteConstant.BYTE_0x1000 != 0) {
            // ForceOnBelt
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mForceOnBelt, offset)
            LogUtil.i(TAG, "forceOnBelt = ${blueRealTimeData.mForceOnBelt}")
            offset += 2
            // PowerOutput
            ByteUtil.intToTwoByteArray(byteArray, blueRealTimeData.mPowerOutput, offset)
            LogUtil.i(TAG, "powerOutput = ${blueRealTimeData.mPowerOutput}")
            offset += 2
        }
        if (flag and ByteConstant.BYTE_0x2000 != 0) {
            // Steps
            ByteUtil.intToThreeByteArray(byteArray, blueRealTimeData.mSteps, offset)
            LogUtil.i(TAG, "steps = ${blueRealTimeData.mSteps}")
            offset += 3
        }
        return byteArray
    }

    private fun getFlagList(list: List<FlagEntity>, flag: Int): List<FlagEntity> {
        val flagList = ArrayList<FlagEntity>()
        if (list.isNotEmpty()) {
            flagList.addAll(list)
        }
        var dataLength = 2
        var resultFlag = ByteConstant.BYTE_0X00
        if (flag and ByteConstant.BYTE_0X01 == 0) {
            dataLength += 2
        } else {
            resultFlag = ByteConstant.BYTE_0X01
        }
        // AverageSpeed
        if (flag and ByteConstant.BYTE_0X02 != 0) {
            resultFlag = resultFlag xor ByteConstant.BYTE_0X02
            dataLength += 2
        }
        // TotalDistance
        if (flag and ByteConstant.BYTE_0X04 != 0) {
            resultFlag = resultFlag xor ByteConstant.BYTE_0X04
            dataLength += 3
        }
        // Inclination and Ramp Angle Setting
        if (flag and ByteConstant.BYTE_0X08 != 0) {
            resultFlag = resultFlag xor ByteConstant.BYTE_0X08
            // Inclination
            dataLength += 2
            // RampAngleSetting
            dataLength += 2
        }
        // ElevationGain
        if (flag and ByteConstant.BYTE_0X10 != 0) {
            resultFlag = resultFlag xor ByteConstant.BYTE_0X10
            // PositiveElevationGain
            dataLength += 2
            // NegativeElevationGain
            dataLength += 2
        }
        // InstantaneousPace
        if (flag and ByteConstant.BYTE_0X20 != 0) {
            resultFlag = resultFlag xor ByteConstant.BYTE_0X20
            dataLength += 2
        }
        // 当前面数据全部存在，数据长度才是19，所以在AveragePace存在时需要计算长度是否超20
        // AveragePace
        if (flag and ByteConstant.BYTE_0X40 != 0) {
            if (!checkLength(dataLength, 2)) {
                return getFlagList(flag, resultFlag, flagList, dataLength)
            }
            resultFlag = resultFlag xor ByteConstant.BYTE_0X40
            dataLength += 2
        }
        // Expended Energy
        if (flag and ByteConstant.BYTE_0X80 != 0) {
            if (!checkLength(dataLength, 6)) {
                return getFlagList(flag, resultFlag, flagList, dataLength)
            }
            resultFlag = resultFlag xor ByteConstant.BYTE_0X80
            // Total Energy
            dataLength += 2
            // Energy per Hour
            dataLength += 2
            // Energy per Minute
            dataLength += 2
        }
        // HeartRate
        if (flag and ByteConstant.BYTE_0x100 != 0) {
            if (!checkLength(dataLength, 1)) {
                return getFlagList(flag, resultFlag, flagList, dataLength)
            }
            resultFlag = resultFlag xor ByteConstant.BYTE_0x100
            dataLength += 1
        }
        // MetabolicEquivalent
        if (flag and ByteConstant.BYTE_0x200 != 0) {
            if (!checkLength(dataLength, 1)) {
                return getFlagList(flag, resultFlag, flagList, dataLength)
            }
            resultFlag = resultFlag xor ByteConstant.BYTE_0x200
            dataLength += 1
        }
        // ElapsedTime
        if (flag and ByteConstant.BYTE_0x400 != 0) {
            if (!checkLength(dataLength, 2)) {
                return getFlagList(flag, resultFlag, flagList, dataLength)
            }
            resultFlag = resultFlag xor ByteConstant.BYTE_0x400
            dataLength += 2
        }
        // RemainingTime
        if (flag and ByteConstant.BYTE_0x800 != 0) {
            if (!checkLength(dataLength, 2)) {
                return getFlagList(flag, resultFlag, flagList, dataLength)
            }
            resultFlag = resultFlag xor ByteConstant.BYTE_0x800
            dataLength += 2
        }
        // Force on Belt and Power Output
        if (flag and ByteConstant.BYTE_0x1000 != 0) {
            if (!checkLength(dataLength, 4)) {
                return getFlagList(flag, resultFlag, flagList, dataLength)
            }
            resultFlag = resultFlag xor ByteConstant.BYTE_0x1000
            // ForceOnBelt
            dataLength += 2
            // PowerOutput
            dataLength += 2
        }
        if (flag and ByteConstant.BYTE_0x2000 != 0) {
            if (!checkLength(dataLength, 3)) {
                return getFlagList(flag, resultFlag, flagList, dataLength)
            }
            resultFlag = resultFlag xor ByteConstant.BYTE_0x2000
            // Steps
            dataLength += 3
        }
        val flagEntity = FlagEntity()
        flagEntity.flag = resultFlag
        flagEntity.dataLength = dataLength
        flagList.add(flagEntity)
        return flagList
    }

    private fun checkLength(dataLength: Int, paraLength: Int): Boolean {
        return dataLength + paraLength <= 20
    }

    private fun getFlagList(
        flag: Int,
        resultFlag: Int,
        flagList: ArrayList<FlagEntity>,
        dataLength: Int
    ): List<FlagEntity> {
        // 获取剩余数据的flag
        // 注意：flag的第一位（bit0）存在数据时，标志为是0，所以需要特别处理一下
        val residualDataFlag = if (flag and ByteConstant.BYTE_0X01 == 0) {
            flag xor resultFlag xor ByteConstant.BYTE_0X01
        } else {
            flag xor resultFlag xor ByteConstant.BYTE_0X00
        }
        val flagEntity = FlagEntity()
        flagEntity.flag = resultFlag
        flagEntity.dataLength = dataLength
        flagList.add(flagEntity)
        return getFlagList(flagList, residualDataFlag)
    }

    private fun initByteArrayWithBlueRealTimeData(blueRealTimeData: BlueRealTimeData): FlagEntity {
        LogUtil.i(TAG, "initByteArrayWithBlueRealTimeData")
        // 初始长度是flag的长度，flag的长度为2
        var byteArrayLength = 2
        var flag = ByteConstant.BYTE_0X00
        if (blueRealTimeData.mInstantaneousSpeed > 0) {
            ByteConstant.BYTE_0X00
            byteArrayLength += 2
        } else {
            ByteConstant.BYTE_0X01
        }

        if (blueRealTimeData.mAverageSpeed > 0) {
            flag = flag xor ByteConstant.BYTE_0X02
            byteArrayLength += 2
        }
        if (blueRealTimeData.mTotalDistance > 0) {
            flag = flag xor ByteConstant.BYTE_0X04
            byteArrayLength += 3
        }
        if (blueRealTimeData.mInclination > 0
            && blueRealTimeData.mRampAngleSetting > 0
        ) {
            flag = flag xor ByteConstant.BYTE_0X08
            byteArrayLength += 2
            byteArrayLength += 2
        }
        if (blueRealTimeData.mPositiveElevationGain > 0
            && blueRealTimeData.mNegativeElevationGain > 0
        ) {
            flag = flag xor ByteConstant.BYTE_0X10
            byteArrayLength += 2
            byteArrayLength += 2
        }
        if (blueRealTimeData.mInstantaneousPace > 0) {
            flag = flag xor ByteConstant.BYTE_0X20
            byteArrayLength += 2
        }
        if (blueRealTimeData.mAveragePace > 0) {
            flag = flag xor ByteConstant.BYTE_0X40
            byteArrayLength += 2
        }
        if (blueRealTimeData.mTotalEnergy > 0
            && blueRealTimeData.mEnergyPerHour > 0
            && blueRealTimeData.mEnergyPerMinute > 0
        ) {
            flag = flag xor ByteConstant.BYTE_0X80
            byteArrayLength += 2
            byteArrayLength += 2
            byteArrayLength += 2
        }
        if (blueRealTimeData.mHeartRate > 0) {
            flag = flag xor ByteConstant.BYTE_0x100
            byteArrayLength += 1
        }
        if (blueRealTimeData.mMetabolicEquivalent > 0) {
            flag = flag xor ByteConstant.BYTE_0x200
            byteArrayLength += 1
        }
        if (blueRealTimeData.mElapsedTime > 0) {
            flag = flag xor ByteConstant.BYTE_0x400
            byteArrayLength += 2
        }
        if (blueRealTimeData.mRemainingTime > 0) {
            flag = flag xor ByteConstant.BYTE_0x800
            byteArrayLength += 2
        }
        if (blueRealTimeData.mForceOnBelt > 0
            && blueRealTimeData.mPowerOutput > 0
        ) {
            flag = flag xor ByteConstant.BYTE_0x1000
            byteArrayLength += 2
            byteArrayLength += 2
        }
        if (blueRealTimeData.mSteps > 0) {
            flag = flag xor ByteConstant.BYTE_0x2000
            byteArrayLength += 3
        }
        val flagEntity = FlagEntity()
        flagEntity.flag = flag
        flagEntity.dataLength = byteArrayLength
        return flagEntity
    }

    private fun configBlueRealTimeDataWithFlag(
        blueRealTimeData: BlueRealTimeData,
        fitnessConfigData: FitnessConfigData
    ) {
        LogUtil.i(TAG, "configBlueRealTimeDataWithFlag")
        blueRealTimeData.mInstantaneousSpeed += 2
        val flag = fitnessConfigData.fitnessMachineFeaturesFlag
        if (flag and ByteConstant.BYTE_0X01 != 0) {
            // Average Speed Supported
            blueRealTimeData.mAverageSpeed += 2
        }
        if (flag and ByteConstant.BYTE_0X04 != 0) {
            // Total Distance Supported
            blueRealTimeData.mTotalDistance += 2
        }
        if (flag and ByteConstant.BYTE_0X08 != 0) {
            // Inclination Supported
            blueRealTimeData.mInclination += 2
            blueRealTimeData.mRampAngleSetting += 2
        }
        if (flag and ByteConstant.BYTE_0X10 != 0) {
            // Elevation Gain Supported
            blueRealTimeData.mPositiveElevationGain += 2
            blueRealTimeData.mNegativeElevationGain += 2
        }
        if (flag and ByteConstant.BYTE_0X20 != 0) {
            // Pace Supported
            blueRealTimeData.mInstantaneousPace += 2
            blueRealTimeData.mAveragePace += 2
        }
        if (flag and ByteConstant.BYTE_0x200 != 0) {
            // Expended Energy Supported
            blueRealTimeData.mTotalEnergy += 2
            blueRealTimeData.mEnergyPerHour += 2
            blueRealTimeData.mEnergyPerMinute += 2
        }
        if (flag and ByteConstant.BYTE_0x400 != 0) {
            // Heart Rate Measurement Supported
            blueRealTimeData.mHeartRate += 2
        }
        if (flag and ByteConstant.BYTE_0x800 != 0) {
            // Metabolic Equivalent Supported
            blueRealTimeData.mMetabolicEquivalent += 2
        }
        if (flag and ByteConstant.BYTE_0x1000 != 0) {
            // Elapsed Time Supported
            blueRealTimeData.mElapsedTime += 2
        }
        if (flag and ByteConstant.BYTE_0x2000 != 0) {
            // Remaining Time Supported
            blueRealTimeData.mRemainingTime += 2
        }
        if (flag and ByteConstant.BYTE_0x8000 != 0) {
            // Force on Belt and Power Output Supported
            blueRealTimeData.mPowerOutput += 2
            blueRealTimeData.mForceOnBelt += 2
        }
        blueRealTimeData.mSteps += 2
    }
}